package com.excel.base;

import com.excel.ServerErrorException;
import com.excel.utils.Cent;
import org.apache.commons.lang3.StringUtils;
import org.apache.poi.ss.usermodel.Cell;
import org.apache.poi.ss.usermodel.CellStyle;
import org.apache.poi.ss.usermodel.DataFormat;
import org.apache.poi.ss.usermodel.Row;
import org.apache.poi.xssf.usermodel.XSSFSheet;
import org.springframework.beans.BeanUtils;

import java.beans.PropertyDescriptor;
import java.lang.reflect.Method;
import java.time.OffsetDateTime;
import java.time.format.DateTimeFormatter;
import java.util.List;
import java.util.Optional;

import static com.excel.utils.Cent.ONE;
import static com.excel.utils.DateTimeConstants.DATE_STRING;

/**
 * @author yutao
 * @since 2020/4/29 5:39 下午
 */
public interface IWmsDownload<U> {

    String COST = "cost";

    String PRICE = "price";

    Integer PRECISION = 100;

    /**
     * 下载库存excel
     * @param stocks
     * @param sheet
     * @param top
     * @throws ReflectiveOperationException
     */
    @SuppressWarnings("checkstyle:methodlength")
    default void download(List<U> stocks, XSSFSheet sheet, String[] top) throws ReflectiveOperationException {
        String[] columnNames = getColumns();
        // 创建表头
        int headerLength = 0;
        if (top != null) {
            headerLength = createTopHeaderRows(sheet, top);
        }
        headerLength = headerLength + createHeaderRows(sheet, headerLength);
        // 设置文本格式
        CellStyle style = sheet.getWorkbook().createCellStyle();
        DataFormat dataFormat = sheet.getWorkbook().createDataFormat();
        style.setDataFormat(dataFormat.getFormat("@"));
        for (int i = 0, stockSize = stocks.size(); i < stockSize; i++) {
            U stock = getStock(stocks, i);
            Row row = sheet.createRow(i + headerLength);
            for (int j = 0, columnLen = columnNames.length; j < columnLen; j++) {
                String columnName = columnNames[j];
                Cell cell = row.createCell(j);
                String value;
                Method method = getMethod(stock, columnName);
                if (method == null) {
                    continue;
                }
                Class type = method.getReturnType();
                Object result = invokeMethod(method, stock);
                if (result == null) {
                    continue;
                }
                if (result instanceof Enum) {
                    value = handleEnumType(method, stock, result);
                    cell.setCellValue(value);
                    continue;
                }
                switch (type.getSimpleName()) {
                    case "String":
                        value = handleStringType(method, stock, columnName);
                        break;
                    case "Long":
                        value = handleLongType(method, stock, columnName);
                        break;
                    case "Integer":
                        value = handleIntegerType(method, stock, columnName);
                        break;
                    case "Short":
                        value = handleShortType(method, stock, columnName);
                        break;
                    case "BigDecimal":
                        value = handleBigDecimalType(method, stock, columnName);
                        break;
                    case "Boolean":
                        value = handleBooleanType(method, stock, columnName);
                        break;
                    case "List":
                        value = handleListType(method, stock, columnName);
                        break;
                    case "Optional":
                        value = handleOptionalType(method, stock, columnName);
                        break;
                    case "Cent":
                        value = handleCentType(method, stock, columnName);
                        break;
                    case "OffsetDateTime":
                        value = handleOffsetDateType(method, stock, columnName);
                        break;
                    default:
                        throw new ServerErrorException("不支持的字段类型");
                }
                cell.setCellValue(value);
                cell.setCellStyle(style);
            }
        }
    }


    /**
     * 处理OffsetDateTime类型
     * @param method
     * @param stock
     * @param columnName
     * @return
     * @throws ReflectiveOperationException
     */
    default String handleOffsetDateType(Method method, U stock, String columnName)
            throws ReflectiveOperationException {
        DateTimeFormatter dateTimeFormatter = DateTimeFormatter.ofPattern(DATE_STRING);
        return dateTimeFormatter.format(((OffsetDateTime) method.invoke(stock)));
    }

    /**
     * 创建表头
     * @param sheet excel表
     * @param headerLength
     * @return 返回表头高度
     */
    default Integer createHeaderRows(XSSFSheet sheet, int headerLength) {
        String[] headerNames = getHeaderNames();
        Row headerRow = sheet.createRow(headerLength);
        for (int i = 0, len = headerNames.length; i < len; i++) {
            headerRow.createCell(i).setCellValue(headerNames[i]);
        }
        return ONE;
    }

    /**
     * 获取excel的表头名
     * @return 返回表头名
     */
    String[] getHeaderNames();

    /**
     * 获取excel的顶级表头名
     * @return
     */
    String[] getTopHeaderNames();

    /**
     * 顶级表头处理
     * @param sheet
     * @param top
     * @return
     */
    default int createTopHeaderRows(XSSFSheet sheet, String[] top) {
        String[] topHeaderNames = getTopHeaderNames();
        Row headerRow = sheet.createRow(0);
        for (int i = 0, j = 0, len = topHeaderNames.length; j < len; i++, j++) {
            headerRow.createCell(i).setCellValue(topHeaderNames[j]);
            headerRow.createCell(++i).setCellValue(top[j]);
        }
        return ONE;
    }

    /**
     * 获取excel每列对应的属性名
     * @return 返回每列的属性名
     */
    String[] getColumns();


    /**
     * 获取stock
     * @param stocks
     * @param stockIndex
     * @return
     */
    default U getStock(List<U> stocks, int stockIndex) {
        return stocks.get(stockIndex);
    }

    /**
     * 获取方法
     * @param stock
     * @param columnName
     * @return
     */
    default Method getMethod(U stock, String columnName) {
        PropertyDescriptor propertyDescriptor = BeanUtils.getPropertyDescriptor(stock.getClass(), columnName);
        if (propertyDescriptor == null) {
            return null;
        }
        return propertyDescriptor.getReadMethod();
    }

    /**
     * 调用方法
     * @param method
     * @param stock
     * @return
     * @throws ReflectiveOperationException
     */
    default Object invokeMethod(Method method, U stock) throws ReflectiveOperationException {
        return method.invoke(stock);
    }

    /**
     * 处理String类型
     * @param getter
     * @param stock
     * @param columnName
     * @return
     * @throws ReflectiveOperationException
     */
    default String handleStringType(Method getter, U stock, String columnName) throws ReflectiveOperationException {
        return (String) getter.invoke(stock);
    }

    /**
     * 处理Long类型
     * @param getter
     * @param stock
     * @param columnName
     * @return
     * @throws ReflectiveOperationException
     */
    default String handleLongType(Method getter, U stock, String columnName) throws ReflectiveOperationException {
        return String.valueOf(getter.invoke(stock));
    }

    /**
     * 处理Integer类型
     * @param getter
     * @param stock
     * @param columnName
     * @return
     * @throws ReflectiveOperationException
     */
    default String handleIntegerType(Method getter, U stock, String columnName) throws ReflectiveOperationException {
        return String.valueOf(getter.invoke(stock));
    }

    /**
     * 处理Short类型
     * @param getter
     * @param stock
     * @param columnName
     * @return
     * @throws ReflectiveOperationException
     */
    default String handleShortType(Method getter, U stock, String columnName) throws ReflectiveOperationException {
        return String.valueOf(getter.invoke(stock));
    }

    /**
     * 处理BigDecimal类型
     * @param getter
     * @param stock
     * @param columnName
     * @return
     * @throws ReflectiveOperationException
     */
    default String handleBigDecimalType(Method getter, U stock, String columnName) throws ReflectiveOperationException {
        return String.valueOf(getter.invoke(stock));
    }

    /**
     * 处理List类型
     * @param getter
     * @param stock
     * @param columnName
     * @return
     * @throws ReflectiveOperationException
     */
    default String handleListType(Method getter, U stock, String columnName) throws ReflectiveOperationException {
        return StringUtils.EMPTY;
    }

    /**
     * 处理Boolean类型
     * @param getter
     * @param stock
     * @param columnName
     * @return
     * @throws ReflectiveOperationException
     */
    default String handleBooleanType(Method getter, U stock, String columnName) throws ReflectiveOperationException {
        return (Boolean) getter.invoke(stock) ? "是" : "否";
    }

    /**
     * 处理Enum类型
     * @param getter
     * @param stock
     * @param enumClz
     * @return
     * @throws ReflectiveOperationException
     */
    default String handleEnumType(Method getter, U stock, Object enumClz) throws ReflectiveOperationException {
        Method getName = enumClz.getClass().getMethod("getName");
        Object enumValue = getName.invoke(enumClz);
        if (enumValue == null) {
            throw new ReflectiveOperationException("枚举名不合法：" + enumClz.getClass().getSimpleName());
        }
        return (String) enumValue;
    }

    /**
     * 处理Optional类型
     * @param getter
     * @param stock
     * @param columnName
     * @return
     * @throws ReflectiveOperationException
     */
    default String handleOptionalType(Method getter, U stock, String columnName) throws ReflectiveOperationException {
        return (String) ((Optional) getter.invoke(stock)).map(Object::toString).orElse("");
    }

    /**
     * 处理Cent类型
     * @param getter
     * @param stock
     * @param columnName
     * @return
     * @throws ReflectiveOperationException
     */
    default String handleCentType(Method getter, U stock, String columnName) throws ReflectiveOperationException {
        return ((Cent) getter.invoke(stock)).toYuan();
    }

}
